package com.hwd.srb.core.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hwd.srb.common.util.JwtUtils;
import com.hwd.srb.common.constant.SmsConstants;
import com.hwd.srb.common.exception.Assert;
import com.hwd.srb.common.result.ResponseEnum;
import com.hwd.srb.common.util.MD5;
import com.hwd.srb.core.enmu.UserStatusEnum;
import com.hwd.srb.core.enmu.UserTypeEnum;
import com.hwd.srb.core.mapper.UserAccountMapper;
import com.hwd.srb.core.mapper.UserInfoMapper;
import com.hwd.srb.core.mapper.UserLoginRecordMapper;
import com.hwd.srb.core.pojo.entity.UserAccount;
import com.hwd.srb.core.pojo.entity.UserInfo;
import com.hwd.srb.core.pojo.entity.UserLoginRecord;
import com.hwd.srb.core.pojo.query.UserInfoQuery;
import com.hwd.srb.core.pojo.vo.LoginVO;
import com.hwd.srb.core.pojo.vo.RegisterVO;
import com.hwd.srb.core.pojo.vo.UserInfoVO;
import com.hwd.srb.core.service.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * <p>
 * 用户基本信息 服务实现类
 * </p>
 *
 * @author 黄伟东/Victor
 * @since 2022-03-10
 */
@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    UserLoginRecordMapper userLoginRecordMapper;

    @Resource
    UserAccountMapper userAccountMapper;

    /**
     * 校验手机验证码
     *
     * @param code   验证码
     * @param mobile 手机号
     * @return boolean 正确返回true，错误返回false
     */
    @Override
    public boolean checkSmsCode(String code, String mobile) {

        boolean checkCode = false;

        // 查询redis中的验证码
        String key = String.format(SmsConstants.REDIS_CODE_KEY_FORMAT, mobile);
        String cacheCode = redisTemplate.opsForValue().get(key);

        // 验证码校验
        if (!StringUtils.isEmpty(cacheCode) && cacheCode.equals(code)) {
            checkCode = true;
        }
        return checkCode;
    }

    /**
     * 用户注册
     *
     * @param registerVO 用户注册信息
     */
    @Transactional
    @Override
    public void register(RegisterVO registerVO) {

        // 校验信息，检验手机号是否已经注册
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getMobile, registerVO.getMobile());
        log.info("正在查询数据库中是否已经注册该手机号");
        Integer count = baseMapper.selectCount(queryWrapper);
        Assert.isTrue(count <= 0, ResponseEnum.MOBILE_EXIST_ERROR);

        // 执行用户注册，保存userInfo，生成userId主键
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(registerVO, userInfo);
        userInfo.setNickName(registerVO.getMobile());
        userInfo.setName(registerVO.getMobile());
        // 对密码进行加密
        userInfo.setPassword(MD5.encrypt(userInfo.getPassword()));
        // 设置一张静态资源服务器上的头像图片
        userInfo.setHeadImg(UserInfo.DEFAULT_HEAD_IMG);
        baseMapper.insert(userInfo);

        // 生成userAccount信息
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(userInfo.getId());

        log.info("正在往数据库插入用户账户信息");
        userAccountMapper.insert(userAccount);
    }

    /**
     * 用户登录
     *
     * @param loginVO 用户登录信息
     * @param ip      ip地址
     * @return com.hwd.srb.core.pojo.vo.UserInfoVO
     */
    @Override
    public UserInfoVO login(LoginVO loginVO, String ip) {
        // 获取用户登录信息
        String mobile = loginVO.getMobile();
        String password = loginVO.getPassword();
        UserTypeEnum userType = loginVO.getUserType();

        // 数据库查询用户详情
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getMobile, mobile)
                .eq(UserInfo::getUserType, userType);
        log.info("正在从数据库中查询用户信息");
        UserInfo userInfo = baseMapper.selectOne(queryWrapper);

        // 校验信息，用户不存在
        Assert.notNull(userInfo, ResponseEnum.LOGIN_MOBILE_ERROR);

        // 用户存在，校验密码
        log.info("用户存在");
        String passwordDb = userInfo.getPassword();
        Assert.isTrue(passwordDb.equals(MD5.encrypt(password)), ResponseEnum.LOGIN_PASSWORD_ERROR);

        // 用户是否被禁用
        // LOGIN_DISABLED_ERROR(-210, "用户已被禁用")
        Assert.equals(userInfo.getStatus(), UserStatusEnum.NORMAL, ResponseEnum.LOGIN_LOKED_ERROR);

        // 根据登录用户的ip生成登录记录
        UserLoginRecord userLoginRecord = new UserLoginRecord();
        userLoginRecord.setUserId(userInfo.getId());
        userLoginRecord.setIp(ip);
        log.info("根据登录用户的ip生成登录记录");
        userLoginRecordMapper.insert(userLoginRecord);

        // 返回用户信息对象
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(userInfo, userInfoVO);

        // 生成token
        userInfoVO.setToken(JwtUtils.createToken(userInfo.getId(), userInfo.getName()));

        return userInfoVO;
    }

    /**
     * 分页查询会员分页列表
     *
     * @param pageParam     分页参数
     * @param userInfoQuery 查询条件对象
     * @return com.baomidou.mybatisplus.core.metadata.IPage<com.hwd.srb.core.pojo.entity.UserInfo>
     */
    @Override
    public IPage<UserInfo> listPage(Page<UserInfo> pageParam, UserInfoQuery userInfoQuery) {
        // 查询条件为null
        if (userInfoQuery == null) {
            return baseMapper.selectPage(pageParam, null);
        }

        // 查询条件不为null，获取查询条件
        String mobile = userInfoQuery.getMobile();
        Integer userType = userInfoQuery.getUserType();
        Integer status = userInfoQuery.getStatus();

        // 创建分页查询条件构造器
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<UserInfo>()
                .like(StringUtils.isNotEmpty(mobile), UserInfo::getMobile, mobile)
                .eq(null != userType, UserInfo::getUserType, userType)
                .eq(null != status, UserInfo::getStatus, status);

        return baseMapper.selectPage(pageParam, queryWrapper);
    }

    /**
     * 解锁或锁定
     *
     * @param id     用户id
     * @param status 状态
     */
    @Transactional
    @Override
    public void lock(Long id, Integer status) {
        // 创建更新条件构造器
        LambdaUpdateWrapper<UserInfo> updateWrapper = new LambdaUpdateWrapper<UserInfo>()
                .set(UserInfo::getStatus, status)
                .eq(UserInfo::getId, id);

        // 执行更新
        baseMapper.update(null, updateWrapper);
    }

    /**
     * 校验手机号是否注册
     *
     * @param mobile 手机号
     * @return boolean
     */
    @Override
    public boolean checkMobile(String mobile) {
        boolean bMobile = true;

        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getMobile, mobile);
        Integer count = baseMapper.selectCount(queryWrapper);
        if (count <= 0) {
            bMobile = false;
        }

        return bMobile;
    }

    /**
     * 根据bindCode获取userId
     *
     * @param bindCode 绑定协议号
     * @return java.lang.Long
     */
    @Override
    public Long getUserIdByBindCode(String bindCode) {
        Long userId = null;
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<UserInfo>()
                .select(UserInfo::getId)
                .eq(UserInfo::getBindCode, bindCode);
        UserInfo userInfo = baseMapper.selectOne(queryWrapper);
        if (Objects.nonNull(userInfo)) {
            userId = userInfo.getId();
        }
        return userId;
    }

    /**
     * 根据bindCode获取userInfo
     *
     * @param bindCode 绑定协议号
     * @return com.hwd.srb.core.pojo.entity.UserInfo
     */
    @Override
    public UserInfo getByBindCode(String bindCode) {
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getBindCode, bindCode);
        return baseMapper.selectOne(queryWrapper);
    }
}
